package cn.funeralobjects.rms.service.impl;

import cn.funeralobjects.aoss.enums.BucketAction;
import cn.funeralobjects.aoss.web.api.BucketApi;
import cn.funeralobjects.aoss.web.model.VBucket;
import cn.funeralobjects.common.model.KeyValue;
import cn.funeralobjects.common.service.annotataion.CommonRollback;
import cn.funeralobjects.common.service.annotataion.CommonService;
import cn.funeralobjects.common.service.exception.LackDataException;
import cn.funeralobjects.common.service.exception.OperationException;
import cn.funeralobjects.rms.entity.ERes;
import cn.funeralobjects.rms.entity.EResInfo;
import cn.funeralobjects.rms.entity.EResOssInfo;
import cn.funeralobjects.rms.enums.ResStatus;
import cn.funeralobjects.rms.model.Res;
import cn.funeralobjects.rms.model.ResOssUploadInfo;
import cn.funeralobjects.rms.model.ResUploadRecord;
import cn.funeralobjects.rms.repository.EResInfoRepository;
import cn.funeralobjects.rms.repository.EResOssInfoRepository;
import cn.funeralobjects.rms.service.ResOssService;
import cn.funeralobjects.rms.service.ResService;
import cn.funeralobjects.rms.utils.VolLevelUtils;
import cn.funeralobjects.util.annotation.ArgHasLength;
import cn.funeralobjects.util.annotation.ArgNotZero;
import cn.funeralobjects.util.annotation.AssertArg;
import cn.funeralobjects.utils.aliyun.oss.OssUtils;
import com.aliyun.oss.model.ObjectMetadata;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.util.Optional;
import java.util.UUID;

/**
 * @author FuneralObjects
 * Create date: 2020/6/7 10:13 AM
 */
@CommonService
public class ResOssServiceImpl implements ResOssService {

    @Resource
    private BucketApi bucketApi;

    private static final String TEMP_PATH = "temp";

    private static final String ROOT_PREFIX = "/";

    @Resource
    private ResService resService;

    @Resource
    private EResOssInfoRepository eResOssInfoRepository;
    @Resource
    private EResInfoRepository eResInfoRepository;


    @CommonRollback
    @AssertArg
    @Override
    public Mono<ResOssUploadInfo> applyUpload(@ArgNotZero Integer groupId, @ArgNotZero Integer stsProdResId, @ArgHasLength String bucketCode, @ArgHasLength String name, @ArgHasLength String path) {
        String objectPath = path.startsWith(ROOT_PREFIX) ? path.substring(1) : path;
        return bucketApi.getIdByCode(bucketCode)
                .switchIfEmpty(Mono.error(new OperationException(String.format("The bucket[%s] cannot be found!", bucketCode))))
                .flatMap(id -> bucketApi.createStsAccess(id, stsProdResId, BucketAction.PUT_OBJECT, objectPath, null))
                .map(vStsAccessKey -> {
                    ResOssUploadInfo resOssUploadInfo = new ResOssUploadInfo().setBucketStsAccessKey(vStsAccessKey).setUploadPath(objectPath);
                    ERes res = resService.add(
                            new Res()
                                    .setResGroupId(groupId)
                                    .setName(name)
                                    .setBucketCode(bucketCode)
                                    .setCode(UUID.randomUUID().toString())
                                    .setStatus(ResStatus.WAITING_UPLOAD), objectPath);
                    return resOssUploadInfo.setUploadId(res.getId()).setUploadCode(res.getCode());
                });
    }

    @CommonRollback
    @AssertArg
    @Override
    public Mono<ResOssUploadInfo> applyUpload(@ArgNotZero Integer groupId, @ArgNotZero Integer stsProdResId, @ArgHasLength String bucketCode, @ArgHasLength String name) {
        return this.applyUpload(groupId, stsProdResId, bucketCode, name, TEMP_PATH + "/" + UUID.randomUUID().toString());
    }

    @CommonRollback
    @AssertArg
    @Override
    public Mono<Void> confirmUpload(@ArgNotZero Integer resId) {
        return Mono.defer(() -> {
            Optional<ResUploadRecord> record = resService.getUploadRecord(resId);
            if (record.isEmpty()) {
                return Mono.empty();
            }
            ResUploadRecord uploadRecord = record.get();
            return bucketApi.findByCode(uploadRecord.getBucketCode())
                    .switchIfEmpty(Mono.error(new LackDataException(String.format("The bucket[%s] doesn't exists", uploadRecord.getBucketCode()))))
                    .flatMap(bucket -> bucketApi.headObjectMeta(bucket.getId(), uploadRecord.getPath()).map(meta -> new KeyValue<>(bucket, meta)))
                    .flatMap(complex -> {
                        ObjectMetadata meta = complex.getValue();
                        VBucket bucket = complex.getKey();
                        EResOssInfo ossInfo = new EResOssInfo()
                                .setBucket(bucket.getName())
                                .setETag(meta.getETag())
                                .setLastModify(meta.getLastModified())
                                .setSize(meta.getContentLength())
                                .setUrl(OssUtils.getObjectUrl("https", bucket.getName(), bucket.getLocation(), uploadRecord.getPath()))
                                .setResId(resId);
                        EResInfo info = new EResInfo()
                                .setActiveCount(0L)
                                .setContentMd5(meta.getContentMD5())
                                .setContentType(meta.getContentType())
                                .setVolLevel(VolLevelUtils.getVolLevel(meta.getContentLength()))
                                .setResId(resId);

                        eResOssInfoRepository.save(ossInfo);
                        eResInfoRepository.save(info);
                        resService.updateStatus(resId, ResStatus.NORMAL);
                        return Mono.empty();
                    });
        });


    }
}
